package wortubung;

import static org.junit.Assert.fail;
import static wortubung.lang.de.Deklination.*;
import static wortubung.lang.de.DeutschSpracheWerkzeug.*;
import static wortubung.util.CommonUtils.*;
import static wortubung.util.MatrixPrinter.*;

import java.util.ArrayList;
import java.util.Properties;

import wortubung.core.LANG;
import wortubung.inflection.DeclensionResult;
import wortubung.lang.LanguageManager;
import wortubung.util.Printable;
import wortubung.util.ToolBase;
import wortubung.util.TransliterateException;

public abstract class DeclensionTestBase {

	protected abstract LANG getLanguage();

	protected DeclensionTestBase() {
		LanguageManager.getAllManagers(ToolBase.getSingleton());
	}

	public LanguageManager getLanguageManager() { return LanguageManager.get(getLanguage()); }
	public Printable getPrintable() { return getLanguageManager().getPrintable(); }

	protected void doDeclensionTest(String propsFile, boolean isNoun, boolean isAdjAdv) throws Exception {
		Properties props = readPropertiesFromCP(propsFile);

		for (Object key : props.keySet()) {
			String word = (String)key;
			String line = props.getProperty(word).trim();
			word = getLanguage().toVernacular(word.replaceAll("\"", ":"));

			int idx = line.indexOf('>');
			if (idx > 0 && idx < line.length()-1)
				line = line.substring(idx+1);

			if (isAdjAdv) {
				// Parse comparative and superlative results
				String[] compSup = getBefore(line, ";").split(",");
				trim(compSup);
				for (int i=compSup.length-1; i>=0; --i) {
					if (compSup[i].contains("-"))
						compSup[i] = compSup[i].replace("-", word);
				}
				getLanguage().toVernacular(compSup);

				// Test the comparative and superlatives
				String[] compSupResult = getComparativeSuperlative(word);
				if (!arrayEq(compSup, compSupResult)) {
					if (compSupResult[0].equals(compSup[0]))
						fail("Superlative of '" + word + "' failed: {" +
							compSupResult[1] + ", " + compSupResult[2] +
							"} versus {" + compSup[1] + ", " + compSup[2] + "}");
					else
						fail("Comparative/superlative of '" + word + "' failed: {" +
							compSupResult[0] + ", " + compSupResult[1] + ", " + compSupResult[2] +
							"} versus {" + compSup[0] + ", " + compSup[1] + ", " + compSup[2] + "}");
				}

				// Get the word declension results; none for adverbs
				line = getAfter(line, ";");
				if (line == null)
					return;
			}

			line = line.trim();
			String root = word;
			DeclensionResult[] expectedResults;
			DeclensionResult[] execResults;
			if (line.startsWith("NORMAL_ADJ_ENDINGS")) {
				if (line.endsWith(">"))
					root = getLanguage().toVernacular(line.substring(line.lastIndexOf("<")+1, line.length()-1));
				expectedResults = fromNormalAdjectiveEndings(root, getLanguage());
			} else {
				expectedResults = parseDeclensionResult(isNoun, line, root);
			}
			if (expectedResults == null)
				fail("Error in '" + propsFile + "' in entry '" + word + "': declention result format.");
			
			// Now, test the word declension
			if (isNoun)
				execResults = new DeclensionResult[]{ declineNoun(word, null) };
			else
				execResults = declineWord(word);

			DeclensionResult[] test = compareResults(execResults, expectedResults);
			if (test != null) {
				if (test.length == 2) {
					Printable p = getPrintable();
					p.promptln("(Expected/Actual)");
				    printMatrix(p, true, true, new Matrix[]{ test[0].toMatrix(getLanguage()), test[1].toMatrix(getLanguage()) }, true, false);
				}
			    fail("Declension of '" + word + "' failed.");
			}
		}
	}

	private static DeclensionResult[] fromNormalAdjectiveEndings(String root, LANG lang) throws TransliterateException {
		DeclensionResult[] ret = {
				getDerAdjTypeSuffix().clone(true),
				getEinAdjTypeSuffix().clone(true),
				getStrongAdjTypeSuffix().clone(true)
		};
		for (DeclensionResult dr : ret)
			dr.instantiateResult(root, lang);
		return ret;
	}

	private DeclensionResult[] parseDeclensionResult(boolean isNoun, String line, String root) throws TransliterateException {
		String[] titles = isNoun ? SP_NAMES : MFNP_NAMES;
		Boolean singleColPl = null;
		String[] nom = null;
		String[] gen = null;
		String[] dat = null;
		String[] akk = null;

		ArrayList<DeclensionResult> list = new ArrayList<DeclensionResult>();
		String[] sa = line.split(";");
		trim(sa);
		String usage = null;
		int idx;
		for (String seg : sa) {
			if (seg.startsWith("{")) {
				if (usage != null) {
					// now save the previous result
					list.add(new DeclensionResult(usage, nom, gen, dat, akk, titles));
					nom = gen = dat = akk = null;
				}
				idx = seg.indexOf("}");
				usage = seg.substring(1, idx);
				String[] tmp = usage.split("\\|");
				if (tmp.length == 2) {
					usage = tmp[0];
					singleColPl = "p".equals(tmp[1]);
				}
				seg = seg.substring(idx+1);
			}
			idx = seg.indexOf(":");
			String sig = seg.substring(0, idx).trim().toLowerCase();
			String[] tmp = seg.substring(idx+1).split(",");
			trim(tmp);
			switch (length(tmp)) {
			case 1:
				titles = (singleColPl == null) ? SorP_NAMES : singleColPl ? P_NAMES : S_NAMES;
				break;
			case 3:
				titles = MFN_NAMES;
				break;
			}
			if (sig.startsWith("nom"))
				nom = tmp;
			else if (sig.startsWith("gen"))
				gen = tmp;
			else if (sig.startsWith("dat"))
				dat = tmp;
			else if (sig.startsWith("akk") || sig.startsWith("acc"))
				akk = tmp;
		}
		if (usage == null) // a single set; but a single set can have a usage as well.
			usage = "";
		list.add(new DeclensionResult(usage, nom, gen, dat, akk, titles));

		for (DeclensionResult dr : list)
			dr.instantiateResult(root, getLanguage());
		return list.toArray(new DeclensionResult[list.size()]);
	}

	/**
	 * @return if failure, return DeclensionResult[2] or DeclensionResult[0]; otherwise, null.
	 */
	private DeclensionResult[] compareResults(DeclensionResult[] execResults, DeclensionResult[] expectedResults) {
		final DeclensionResult[] ERROR = new DeclensionResult[0];

		int len = length(execResults);
		if (len != length(expectedResults))
			return ERROR;

		if (len == 1) {
			if (array2dEq(execResults[0].getResults(getLanguage()), expectedResults[0].getResults(getLanguage())))
				return null;
			return new DeclensionResult[]{ execResults[0], expectedResults[0] };
		}

		for (DeclensionResult xr : execResults) {
			String usage = xr.getUsage();
			DeclensionResult er = null;
			for (DeclensionResult er1 : expectedResults) {
				if (eq(er1.getUsage(), usage, false)) {
					er = er1;
					break;
				}
			}
			if (er == null)
				return ERROR;
			if (!array2dEq(xr.getResults(getLanguage()), er.getResults(getLanguage())))
				return new DeclensionResult[]{ xr, er };
		}

		return null;
	}
}
